# LoadingAnimation - Animation widget indicates that Entertainer is loading
# Copyright (C) 2007 Lauri Taimila
# 
# Entertainer is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# Entertainer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import math
import cairo
import clutter
import gobject
from clutter.cluttercairo import CairoTexture

class LoadingAnimation(CairoTexture):
    """
    Loading animation widget
    
    This widget is used when we want to tell user that Entertainer is loading.
    """
    
    def __init__(self, size, fg_color, bg_color, inner_radius = 0.25, outter_radius = 0.45, thickness = 0.08):
        """
        Initialize ArrowTexture
        @param size: Width and height of the texture
        @param fg_color: Foreground color
        @param bg_color: Background color
        @param inner_radius: Distance from origo to line start point 0-1
        @param outter_radius: Distance from origo to line end point 0-1
        @param thinkness: Lines thickness 0-1
        """
        CairoTexture.__init__(self, size, size)
        self.set_anchor_point(size /2, size /2)
      
        c = self._clutterColorToCairoColor(fg_color)
        bg = self._clutterColorToCairoColor(bg_color)

        context = self.cairo_create()
        context.scale(size, size)
        context.set_line_width (thickness)
        context.set_line_cap(cairo.LINE_CAP_ROUND)
        
        radius = outter_radius    # radius of larger circle
        in_radius = inner_radius  # radius of smaller circle
        
        # Background color
        context.set_source_rgba(bg[0], bg[1], bg[2], bg[3])
        
        #FIXME: Refactor line drawing to be done in a one loop
        angle =  0 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x, y)
        context.stroke()
        
        angle =  30 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x, y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.9)
        angle =  60 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.8)
        angle =  90 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.7)
        angle =  120 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.6)
        angle =  150 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x, y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.5)
        angle =  180 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.4)
        angle =  210 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.3)
        angle =  240 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.2)
        angle =  270 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3] - 0.1)
        angle =  300 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        context.set_source_rgba(c[0], c[1], c[2], c[3])
        angle =  330 * (math.pi / 180.0)
        context.arc(0.5, 0.5, in_radius, angle, angle)
        (x,y) = context.get_current_point()
        context.new_path()
        context.arc(0.5, 0.5, radius, angle, angle)
        context.line_to(x,y)
        context.stroke()
        
        del(context) # Updates texture
    
        self.keep_going = False
    
    def _clutterColorToCairoColor(self, color):
        """
        Transform from 0-255 to 0-1.
        @param color: clutter.Color
        """
        r = float(color.red) / 255.0
        g = float(color.green) / 255.0
        b = float(color.blue) / 255.0
        a = float(color.alpha) / 255.0
        return [r,g,b,a]
    
    def _rotate_throbber(self):
        """
        Update texture.
        """
        r = self.get_rotation(clutter.Z_AXIS)
        self.set_rotation(clutter.Z_AXIS, 30 + int(r[0]), 0, 0, 0)
        return self.keep_going
    
    def hide(self):
        """
        Hide throbber and stop animation.
        """
        self.keep_going = False
        CairoTexture.hide(self)
        
    def show(self):
        """
        Show throbber and activate animation
        """
        self.keep_going = True
        gobject.timeout_add(50, self._rotate_throbber)
        CairoTexture.show(self)